#include "StdAfx.h"
#include "PoomPictureLoader.h"

#include <imaging.h>
#include "PictureSaver.h"
#include "Theme.h"

#ifdef _DEBUG
#include "crtdbg.h"
#endif

bool PoomPictureLoader::GetContactPicture(IPOutlookApp2* polApp, CEOID contactOid, HDC windowDC, HBITMAP& hbm, int& width, int& height)
{
   if (polApp == NULL)
      return false;
   
   HRESULT   hr;
   IItem*    pItem = NULL;
   IStream*  pStream = NULL;
   ULONG     ulSize;

   hr = polApp->GetItemFromOidEx(contactOid, 0, &pItem);
   if (FAILED(hr))
      return false;

   // Extract the picture from the contact
   hr = pItem->OpenProperty(PIMPR_PICTURE, GENERIC_READ, &pStream);
   if (FAILED(hr))
   {
      pItem->Release();
      return false;
   }

   hr = GetStreamSize(pStream, ulSize);
   if (FAILED(hr) || ulSize <= 0)
   {
      pStream->Release();
      pItem->Release();
      return false;
   }

   UINT iWidth = width;
   UINT iHeight = height;
   hr = GetBitmapFromStream(pStream, windowDC, hbm, iWidth, iHeight);

   pStream->Release();
   pItem->Release();

   if (FAILED(hr))
   {
     return false;
   }

   width = iWidth;
   height = iHeight;
   return true;
}

bool PoomPictureLoader::SetContactPicture(IPOutlookApp2* polApp, CEOID contactOid, const TCHAR* sFile, EPicSaveType size)
{
  if (polApp == NULL || sFile == NULL)
    return false;

  HRESULT   hr;
  // load image
  IImage* pImage = NULL;
  if (!PictureSaver::LoadImageFromFile(&pImage, sFile))
    return false;
  // prepare thumbnail
  ImageInfo ii;
  hr = pImage->GetImageInfo(&ii);
  if (FAILED(hr) || ii.Width == 0 || ii.Height == 0)
  {
    pImage->Release();
    return false;
  }
  int width, height;

  switch(size)
  {
  case pstSmall:
    width = height = 64;
    break;
  case pstNormal:
    width = ii.Width;
    height = ii.Height;
    if (width * height > 10000)
    {
      double k = sqrt(double(ii.Width) / double(ii.Height));
      width =  int(100.0 * k);
      height = int(100.0 / k);
    }
    if (width < Theme::iDetailsPictureWidth && height < Theme::iDetailsHeaderSize)
    {
      width = Theme::iDetailsPictureWidth;
      height = Theme::iDetailsHeaderSize;
    }
    break;
  case pstFullscreen:
    width = ::GetSystemMetrics(SM_CXSCREEN);
    height = ::GetSystemMetrics(SM_CYSCREEN);
    break;
  case pstOriginal:
    width = ii.Width;
    height = ii.Height;
    break;
  }
  // get thumbnail
  IImage* pThumb = NULL;
  if (width == ii.Width && height == ii.Height)
  {
    pThumb = pImage;
    pImage = NULL;
  }
  else
  {
    if (!PictureSaver::ResizeImage(pImage, &pThumb, width, height, false))
    {
      pImage->Release();
      return false;
    }
  }

  // assign image to contact
  IItem*    pItem = NULL;
  IStream*  pStream = NULL;

  hr = polApp->GetItemFromOidEx(contactOid, 0, &pItem);
  if (FAILED(hr))
    return false;

  // Extract the picture from the contact
  hr = pItem->OpenProperty(PIMPR_PICTURE, GENERIC_WRITE, &pStream);
  if (FAILED(hr))
  {
    pItem->Release();
    return false;
  }

  EImageFormat fmt = PictureSaver::GetImageFormat(sFile);
  if (fmt == ifNone)
    fmt = ifJpeg;
  bool result = PictureSaver::SaveImageToStream(pThumb, pStream, fmt);

  pThumb->Release();

  if (result)
  {
    hr = pStream->Commit(0);
    if (SUCCEEDED(hr))
    {
      hr = pItem->Save();
    }

    if (FAILED(hr))
      result = false;
  }

  pStream->Release();
  pItem->Release();

  return result;
}

// **************************************************************************
// Function Name: GetStreamSize
// 
// Purpose: Given an IStream, returns the size of the stream.  This is needed
//          for streams that do not support the Stat method
//
// Arguments:
//    IN  IStream*  pStream - stream to determine size for
//    OUT ULONG*    pulSize - size of stream
//
// Return Values:
//    HRESULT - S_OK if success, failure code if not
//
// Side Effects:
//    The stream pointer always resets to the beginning
//

HRESULT PoomPictureLoader::GetStreamSize(IStream* pStream, ULONG& ulSize)
{
    HRESULT hr;
    LARGE_INTEGER  li = {0};
    ULARGE_INTEGER uliZero = {0};
    ULARGE_INTEGER uli;

    if (pStream == NULL)
      return E_FAIL;

    hr = pStream->Seek(li, STREAM_SEEK_END, &uli);
    if (FAILED(hr))
      return hr;

    ulSize = uli.LowPart;
    // Move the stream back to the beginning of the file
    hr = pStream->Seek(li, STREAM_SEEK_SET, &uliZero);

    return hr;
}


// **************************************************************************
// Function Name: GetBitmapFromStream
// 
// Purpose: Convert an IStream to an HBITMAP and return the new dimensions
//
// Arguments:
//    IN     UINT  uFitToWidth     - width of source image
//    IN     UINT  uFitToHeight    - height of source image
//    OUT UINT* puWidth  - width of image to scale to
//    OUT UINT* puHeight - height of image to scale to
//
// Return Values:
//    HRESULT - S_OK if success, failure code if not
//
HRESULT PoomPictureLoader::GetBitmapFromStream(IStream* pStream, HDC windowDC, HBITMAP& hBitmap, UINT& uWidth, UINT& uHeight)
{
    HRESULT hr;

    IImagingFactory*  pFactory = NULL;
    IImage*           pImage   = NULL;
    IImage*           pThumbnail = NULL;
    ImageInfo         imgInfo = {0};

    if (pStream == NULL)
      return E_FAIL;

    // Use a little imaging help
    hr = CoCreateInstance(CLSID_ImagingFactory, NULL, CLSCTX_INPROC_SERVER, IID_IImagingFactory, (void**) &pFactory);
    if (FAILED(hr))
       return hr;
    
    hr = pFactory->CreateImageFromStream(pStream, &pImage);
    if (FAILED(hr))
    {
       pFactory->Release();
       return hr;
    }

    hr = pImage->GetImageInfo(&imgInfo);
    if (FAILED(hr))
    {
       pFactory->Release();
       pImage->Release();
       return hr;
    }
    if (imgInfo.Width <= 0 || imgInfo.Height <= 0)
    {
       pFactory->Release();
       pImage->Release();
       return E_FAIL;
    }

    // Scale to the new size
    if (!PictureSaver::ScaleProportional(uWidth, uHeight, imgInfo.Width, imgInfo.Height))
    {
       pFactory->Release();
       pImage->Release();
       return E_FAIL;
    }

    // Get the new image
    hr = pImage->GetThumbnail(imgInfo.Width, imgInfo.Height, &pThumbnail);
    if (FAILED(hr))
    {
       pFactory->Release();
       pImage->Release();
       return hr;
    }

    // Convert this to HBITMAP, our target format
    hr = GetBitmapFromImage(pThumbnail, windowDC, hBitmap, RGB(0,0,0));

    pFactory->Release();
    pImage->Release();
    pThumbnail->Release();

    if (FAILED(hr))
    {
       return E_FAIL;
    }

    uWidth = imgInfo.Width;
    uHeight = imgInfo.Height;

    return S_OK;
}

// **************************************************************************
// Function Name: HBITMAPFromImage
// 
// Purpose: Convert IImage to HBITMAP.  If bitmap has transparency, the
//    background will be filled with the color passed in
//
// Arguments:
//    IN  IImage*   pImage      - pointer to the IImage
//    IN  COLORREF  crBackColor - color of the background
//
// Return Values:
//    HRESULT - S_OK if success, failure code if not
//
HRESULT PoomPictureLoader::GetBitmapFromImage(IImage * pImage, HDC windowDC, HBITMAP& hBitmap, COLORREF crBackColor)
{
    HRESULT    hr;
    ImageInfo  ii;
    void *     pv;
    BITMAPINFO bmi = { 0 };
    HDC        hdc;
    HBITMAP    hbmOld = NULL;
    RECT       rc = { 0 };

    if (pImage == NULL)
      return E_FAIL;

    // Get image width/height
    hr = pImage->GetImageInfo(&ii);
    if (FAILED(hr))
      return E_FAIL;

    // Create HDC
    hdc = CreateCompatibleDC(windowDC);
    if (hdc == NULL)
      return E_FAIL;

    // Create DIB section
    bmi.bmiHeader.biSize        = sizeof(BITMAPINFOHEADER);
    bmi.bmiHeader.biWidth       = ii.Width;
    bmi.bmiHeader.biHeight      = ii.Height;
    bmi.bmiHeader.biPlanes      = 1;
    bmi.bmiHeader.biBitCount    = (SHORT) max(16, GetDeviceCaps(hdc, BITSPIXEL));
    bmi.bmiHeader.biCompression = BI_RGB;

    hBitmap = CreateDIBSection(hdc, &bmi, DIB_RGB_COLORS, &pv, NULL, 0);
    if (hBitmap == NULL)
    {
      DeleteDC(hdc);
      hdc = NULL;
      return E_FAIL;
    }

    // Select DIB into DC
    hbmOld = (HBITMAP)SelectObject(hdc, hBitmap);

    rc.right = ii.Width;
    rc.bottom = ii.Height;

    // Clear the bitmap using the background color
    //FillRect(hdc, &rc, crBackColor);

    // Draw into DC/DIB
    hr = pImage->Draw(hdc, &rc, NULL);

    SelectObject(hdc, hbmOld);
    DeleteDC(hdc);

    if (FAILED(hr))
    {
      DeleteObject(hBitmap);
      hBitmap = NULL;
      return E_FAIL;
    }

    return S_OK;
}